/****************************************************************************

 Copyright (C) 2009 Jean Pierre Charalambos. All rights reserved.

 Jean Pierre Charalambos (http://dis.unal.edu.co/profesores/pierre/) is an
 Affiliate Associate Professor of the Faculty of Engineering at National
 University of Colombia (http://www.unal.edu.co/).

 This file is part of maku version 0.1.

 http://code.google.com/p/maku/
 email: jpcharalambosh@unal.edu.co

 This file may be used under the terms of the GNU General Public License 
 versions 2.0 or 3.0 as published by the Free Software Foundation and
 appearing in the COPYING file included in the packaging of this file.

 This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

*****************************************************************************/

#include "scenebase.h"

#include <math.h>

#include <QAction>
#include <QMenu>
#include <QDir>

void SceneBase::draw() const {
  #ifdef WITH_GLEW
   if (hasOpenGL2)
     glUseProgram(p);
  #endif
  drawMesh();
  #ifdef WITH_GLEW
   if (hasOpenGL2)
     glUseProgram(0);
  #endif
}

#ifdef WITH_GLEW
void SceneBase::setShader(const QString & shader) const {
  //OpenGL es flexible aca: no importa si los shaders no han sido creados ;-)
  glDetachShader(p,v);
  glDetachShader(p,f);
  glDeleteShader(v);
  glDeleteShader(f);
  glDeleteProgram(p);
  if (shader != "fixed functionality") {
    p = glCreateProgram();
    v = glCreateShader(GL_VERTEX_SHADER); 
    f = glCreateShader(GL_FRAGMENT_SHADER);
    QString dir = "/usr/share/maku/shaders/";
    QDir shaderDir(dir);
    if ( shaderDir.count() == 0 ) {
      dir = "/usr/local/share/maku/shaders/";
      shaderDir.setPath(dir);
      if ( shaderDir.count() == 0 ) {
	dir = shaderDir.homePath();
	dir = shaderDir.toNativeSeparators(dir);
	dir = dir + "/share/maku/shaders/";
	shaderDir.setPath(dir);
	if ( shaderDir.count() == 0 ) {
          dir = "shaders/";
          shaderDir.setPath(dir);
          if ( shaderDir.count() == 0 ) {
	    //std::cout<< "Didn't found shaders directory"<<std::endl;
	    exit(1);
          }
	}
      }
    }
    QString vertexExtension = ".vert";
    QString fragmentExtension = ".frag";
    QString vertexShader = dir + shader + vertexExtension;
    QString fragmentShader = dir + shader + fragmentExtension;
    //2.1 Convert it to the right encoding:
    QByteArray ba1=(readShaderCode(vertexShader)).toLocal8Bit();
    QByteArray ba2=(readShaderCode(fragmentShader)).toLocal8Bit();
    //2.2 Allocate enough memory:
    vs=new char[ba1.size() + 1];
    fs=new char[ba2.size() + 1];
    //2.3 Copy them:
    strcpy(vs,  ba1.data());
    strcpy(fs,  ba2.data());

    const char * vv = vs;
    const char * ff = fs;

    glShaderSource(v, 1, &vv,NULL);
    glShaderSource(f, 1, &ff,NULL);

    free(vs);
    free(fs);

    glAttachShader(p,v);
    glAttachShader(p,f);

    glCompileShader(v);
    glCompileShader(f);

    glLinkProgram(p);
  }
  emit changed();
}

QString SceneBase::readShaderCode(const QString & name) const {
  QString text;
  QFile file(name);
  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    exit(1);

  QTextStream in(&file);
  text = in.readAll();
  file.close();

  return text;
}
#endif

void SceneBase::setDefaultMaterial(void) const {
  GLfloat mat_a[] = {0.1, 0.1, 0.1, 1.0};
  GLfloat mat_d[] = {0.7, 0.7, 0.5, 1.0};
  GLfloat mat_s[] = {1.0, 1.0, 1.0, 1.0};
  GLfloat shine[] = {120.0};

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   mat_a);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   mat_d);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  mat_s);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
}

void SceneBase::createActions() {
  pointColorDialogAct = new QAction(tr("Point col&or"), this);
  pointColorDialogAct->setShortcut(tr("Ctrl+L"));
  pointColorDialogAct->setStatusTip(tr("Set point color"));
  connect(pointColorDialogAct, SIGNAL(triggered()), this, SLOT(vertexColors()));
  pointColorDialogAct->setEnabled(false);

  openTexAct = new QAction(tr("&Texture"), this);
  openTexAct->setShortcut(tr("Ctrl+T"));
  openTexAct->setStatusTip(tr("Open texture"));
  connect(openTexAct, SIGNAL(triggered()), this, SLOT(textureHandling()));
  openTexAct->setEnabled(false);

  decimateAct = new QAction(tr("&Decimater"), this);
  decimateAct->setShortcut(tr("Ctrl+D"));
  decimateAct->setStatusTip(tr("Decimate scene"));
  connect(decimateAct, SIGNAL(triggered()), this, SLOT(simplifyModel()));
  decimateAct->setEnabled(false);

  detachTexAct = new QAction(tr("Detach Textu&re"), this);
  detachTexAct->setShortcut(tr("Ctrl+U"));
  detachTexAct->setStatusTip(tr("Detach texture"));
  connect(detachTexAct, SIGNAL(triggered()), this, SLOT(textureHandling()));
  detachTexAct->setEnabled(false);
  detachTexAct->setVisible(false);

  drawModesActs = new QActionGroup(this);

  stripArraysAct = new QAction(tr("Strips"), drawModesActs);
  stripArraysAct->setCheckable(true);
  coloredStripsAct = new QAction(tr("Colored Strips"), drawModesActs);
  coloredStripsAct->setCheckable(true);
  pointsAct = new QAction(tr("Points"), drawModesActs);
  pointsAct->setCheckable(true);
  connect(pointsAct, SIGNAL(toggled(bool)), this, SLOT(points(bool)));
  wiredAct = new QAction(tr("Wired Frame"), drawModesActs);
  wiredAct->setCheckable(true);
  hiddenLineAct = new QAction(tr("hidden Line"), drawModesActs);
  hiddenLineAct->setCheckable(true);
  solidFlatAct = new QAction(tr("Solid Flat"), drawModesActs);
  solidFlatAct->setCheckable(true);
  solidSmoothAct = new QAction(tr("Solid Smooth"), drawModesActs);
  solidSmoothAct->setCheckable(true);
  coloredVerticesAct = new QAction(tr("Colored Vertices"), drawModesActs);
  coloredVerticesAct->setCheckable(true);
  solidColoredFacesAct = new QAction(tr("Solid Colored Faces"), drawModesActs);
  solidColoredFacesAct->setCheckable(true);
  smoothColoredFacesAct = new QAction(tr("Smooth Colored Faces"), drawModesActs);
  smoothColoredFacesAct->setCheckable(true);

  drawModesActs->setEnabled(false);
  connect(drawModesActs, SIGNAL(triggered(QAction *)), this, SLOT(currentAction(QAction *)) );

  boundingGroupActs = new QActionGroup(this);
  boundingGroupActs->setExclusive(false);
  boundingBoxAct = new QAction(tr("Draw &bounding box"), boundingGroupActs);
  boundingBoxAct->setShortcut(tr("Ctrl+B"));
  boundingBoxAct->setCheckable(true);
  connect(boundingBoxAct, SIGNAL(toggled(bool)), this, SLOT(drawBoundingBox(bool)));
  boundingSphereAct = new QAction(tr("Draw bounding sp&here"), boundingGroupActs);
  boundingSphereAct->setShortcut(tr("Ctrl+H"));
  boundingSphereAct->setCheckable(true);
  connect(boundingSphereAct, SIGNAL(toggled(bool)), this, SLOT(drawBoundingSphere(bool)));

  menu = new QMenu(tr("&Scene"));
  menu->addActions(boundingGroupActs->actions());
  menu->addSeparator();
  menu->addAction(openTexAct);
  menu->addAction(detachTexAct);
  menu->addSeparator();
  menu->addAction(decimateAct);
  menu->addSeparator();
  menu->addActions(drawModesActs->actions());
}

void SceneBase::vertexColors() {
  QColor color = QColorDialog::getColor(Qt::white, 0);
  if (color.isValid())
    pointColor = color;
}

void SceneBase::currentAction(QAction * cA) {
    if(cA == stripArraysAct)
      drawMode = StripArrays;
    if(cA == coloredStripsAct)
      drawMode = ColoredStrips;
    if(cA == wiredAct)
      drawMode = Wired;
    if(cA == hiddenLineAct)
      drawMode = HiddenLine;
    if(cA == solidFlatAct)
      drawMode = SolidFlat;
    if(cA == solidSmoothAct)
      drawMode = SolidSmooth;
    if(cA == coloredVerticesAct)
      drawMode = ColoredVertices;
    if(cA == solidColoredFacesAct)
      drawMode = SolidColoredFaces;
    if(cA == smoothColoredFacesAct)
      drawMode = SmoothColoredFaces;
    emit changed();
}

void SceneBase::textureHandling() {
  if (openTexAct->isVisible()) {
    QString fileName = QFileDialog::getOpenFileName(0, "Choose a texture to open", "", "Images (*.bmp *.BMP *.gif *.GIF *.jpg *.JPG *.jpeg *.JPEG *.png *.PNG *.pbm *.PBM *.pgm *.PGM *.ppm *.PPM *.tiff *.TIFF *.xbm *.XBM *.xpm *.XPM)");
    if( ! fileName.isNull() ) {
      openTexture(fileName);
      openTexAct->setVisible(false);
      openTexAct->setEnabled(false);
      detachTexAct->setVisible(true);
      detachTexAct->setEnabled(true);
      emit changed();
    }
  }
  else {
    detachTexture();
    detachTexAct->setVisible(false);
    detachTexAct->setEnabled(false);
    openTexAct->setVisible(true);
    openTexAct->setEnabled(true);
    emit changed();
  }
}
